#include "oled_12832.h"

#include "api_inc_i2c.h"
#include "api_hal_i2c.h"
#include "api_os.h"

/*
      Seg0 Seg1 Seg2 ...... Seg125 Seg126 Seg127
Page0  X
Page1  X
Page2  X
Page3  X

*/

#define  OLED_ADDR        (0x3C)
#define  OLED_IIC         (I2C3)
#define  OLED_IIC_TIMEOUT (10000)

// 显示范围
enum {
    kOledPageStart = 0,      // Page 起始
    kOledPageEnd   = 3,      // Page 结束
    kOledSegStart  = 0,      // Seg  起始
    kOledSegEnd    = 127,    // Seg  结束
};

// 配置OLED使用的I2C
static bool Oled_I2cInit(void)
{
    I2C_Config_t config;

    config.freq = I2C_FREQ_400K;

    return I2C_Init(OLED_IIC, config);
}

// OLED 写命令
static void Oled_WriteCmd(uint8_t Cmd)
{
    I2C_WriteMem(OLED_IIC, OLED_ADDR, 0x00, 1, &Cmd, 1, OLED_IIC_TIMEOUT);
}

// OLED 写数据
static void Oled_WriteDat(uint8_t Dat)
{
    I2C_WriteMem(OLED_IIC, OLED_ADDR, 0x40, 1, &Dat, 1, OLED_IIC_TIMEOUT);
}

void Oled_Init(void)
{
    Oled_I2cInit();
    
    OS_Sleep(100);  // 延时等待系统稳定
    
    Oled_WriteCmd(0xAE); // Set display off
  
    Oled_WriteCmd(0x40); // 00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
    Oled_WriteCmd(0xB0); // Set Page Start Address for Page Addressing Mode,0-7
    
    Oled_WriteCmd(0xC8); // Set COM Output Scan Direction
    
    Oled_WriteCmd(0x81); // set contrast control register 
    Oled_WriteCmd(0xFF);
    
    Oled_WriteCmd(0xA1); // --set start line address
    
    Oled_WriteCmd(0xA6); // --set contrast control register
    
    Oled_WriteCmd(0xA8); // 0x00~0xff
    Oled_WriteCmd(0x1F); // --set segment re-map 0 to 127
    
    Oled_WriteCmd(0xD3); // --set normal display
    Oled_WriteCmd(0x00); // --set multiplex ratio(1 to 64)
    
    Oled_WriteCmd(0xD5); //
    Oled_WriteCmd(0xF0); // 0xa4,Output follows RAM content;0xa5,Output ignores RAM content
    
    Oled_WriteCmd(0xD9); // -set display offset
    Oled_WriteCmd(0x22); // -not offset
    
    Oled_WriteCmd(0xDA); // --set display clock divide ratio/oscillator frequency
    Oled_WriteCmd(0x02); // --set divide ratio
    
    Oled_WriteCmd(0xDB); // --set pre-charge period
    Oled_WriteCmd(0x49); //
    
    Oled_WriteCmd(0x8D); // --set com pins hardware configuration
    Oled_WriteCmd(0x14);

    Oled_WriteCmd(0xAF); // Set display on

    Oled_Clear();
}

void Oled_On(void)
{
    Oled_WriteCmd(0x8D); // Enable charge pump
    Oled_WriteCmd(0x14);
    
    Oled_WriteCmd(0xAF); // Display on
}

void Oled_Off(void)
{
    Oled_WriteCmd(0x8D); // Disable charge pump
    Oled_WriteCmd(0x10);

    Oled_WriteCmd(0xAE); // Display off
}

void Oled_SetPosition(uint8_t page, uint8_t seg)
{
    if (seg > kOledSegEnd)
        seg = 0;
    
    if (page > kOledPageEnd)
        page = 0;

    // Enter page address page0 ~ 7
    Oled_WriteCmd(0xB0 + page);

    // Enter seg address seg0 ~ 127
    Oled_WriteCmd((seg >> 4) | 0x10);
    Oled_WriteCmd(seg & 0x0F);
}

void Oled_Fill(uint8_t AData)
{
    uint8_t page,n;
    
    for (page = 0; page <= kOledPageEnd; page++)
    {
        Oled_WriteCmd(0xB0 + page);   // Set page

        Oled_WriteCmd(0x00);          // low column start address
        Oled_WriteCmd(0x10);          // high column start address
        
        for (n = 0; n < 128; n++)
            Oled_WriteDat(AData);
    }
}

void Oled_Clear(void)
{
    Oled_Fill(0x00);
}

// 展示单个6X8字符
void Oled_ShowAsciiSize6X8(uint8_t page, uint8_t seg, char AChar)
{
    // 可见字符
    if (AChar >= 0x20 && AChar <= 0x7E)
    {
        uint8_t i = 0;
        uint8_t Index = AChar - 0x20;

        Oled_SetPosition(page, seg);

        for (i = 0; i < 6; i++)
            Oled_WriteDat(F6x8[Index][i]);
    }
}

void Oled_ShowAsciiSize8X16(uint8_t page, uint8_t seg, char AChar)
{
    // 可见字符
    if (AChar >= 0x20 && AChar <= 0x7E)
    {
        uint8_t i = 0;
        uint8_t Index = AChar - 0x20;

        Oled_SetPosition(page, seg);

        for (i = 0; i < 8; i++)
            Oled_WriteDat(F8X16[Index][i]);

        page++; // 下一页
        Oled_SetPosition(page, seg);

        for (i = 8; i < 16; i++)
            Oled_WriteDat(F8X16[Index][i]);
    }  
}

void Oled_ShowIcon16X16(uint8_t page, uint8_t seg, const uint8_t* AData)
{
    if (AData != NULL)
    {
        uint8_t i = 0;
        if (page > kOledPageEnd)
            page = kOledPageStart;

        // 列溢出，回头
        if ((seg + 16) > kOledSegEnd)
            seg = kOledSegStart;

        Oled_SetPosition(page, seg);

        for (i = 0; i < 16; i++)
            Oled_WriteDat(*AData++);
        
        // 行溢出，回头
        page++;
        if (page > kOledPageEnd)
            page = kOledPageStart;

        Oled_SetPosition(page, seg);

        for (i = 16; i < 32; i++)
            Oled_WriteDat(*AData++);
    }
}

void Oled_ShowString8X16(uint8_t page, uint8_t seg, const uint8_t* AData)
{
    if (*AData != NULL)
    {
        while(*AData != '\0')
        {
            Oled_ShowAsciiSize8X16(page, seg, *AData++);
            seg += 8;

            if (seg > 120)
                seg = 0;
        }
    }
}
